{
  runTest,
  forgejoPackage,
}:

let
  ## gpg --faked-system-time='20230301T010000!' --quick-generate-key snakeoil ed25519 sign
  signingPrivateKey = ''
    -----BEGIN PGP PRIVATE KEY BLOCK-----

    lFgEY/6jkBYJKwYBBAHaRw8BAQdADXiZRV8RJUyC9g0LH04wLMaJL9WTc+szbMi7
    5fw4yP8AAQCl8EwGfzSLm/P6fCBfA3I9znFb3MEHGCCJhJ6VtKYyRw7ktAhzbmFr
    ZW9pbIiUBBMWCgA8FiEE+wUM6VW/NLtAdSixTWQt6LZ4x50FAmP+o5ACGwMFCQPC
    ZwAECwkIBwQVCgkIBRYCAwEAAh4FAheAAAoJEE1kLei2eMedFTgBAKQs1oGFZrCI
    TZP42hmBTKxGAI1wg7VSdDEWTZxut/2JAQDGgo2sa4VHMfj0aqYGxrIwfP2B7JHO
    GCqGCRf9O/hzBA==
    =9Uy3
    -----END PGP PRIVATE KEY BLOCK-----
  '';
  signingPrivateKeyId = "4D642DE8B678C79D";

  metricSecret = "fakesecret";

  base =
    {
      lib,
      pkgs,
      type,
      ...
    }:

    {
      name = "forgejo-${type}";
      meta.maintainers = with lib.maintainers; [
        bendlas
        emilylange
        tebriel
      ];

      nodes = {
        server =
          { config, pkgs, ... }:
          {
            virtualisation.memorySize = 2047;
            services.forgejo = {
              enable = true;
              package = forgejoPackage;
              database = { inherit type; };
              settings.service.DISABLE_REGISTRATION = true;
              settings."repository.signing".SIGNING_KEY = signingPrivateKeyId;
              settings.actions.ENABLED = true;
              settings.repository = {
                ENABLE_PUSH_CREATE_USER = true;
                DEFAULT_PUSH_CREATE_PRIVATE = false;
              };
              settings.metrics.ENABLED = true;
              secrets.metrics.TOKEN = pkgs.writeText "metrics_secret" metricSecret;
            };
            environment.systemPackages = [
              config.services.forgejo.package
              pkgs.gnupg
              pkgs.jq
              pkgs.file
            ];
            services.openssh.enable = true;

            specialisation.runner = {
              inheritParentConfig = true;
              configuration.services.gitea-actions-runner = {
                package = pkgs.forgejo-runner;
                instances."test" = {
                  enable = true;
                  name = "ci";
                  url = "http://localhost:3000";
                  labels = [
                    # type ":host" does not depend on docker/podman/lxc
                    "native:host"
                  ];
                  tokenFile = "/var/lib/forgejo/runner_token";
                };
              };
            };
            specialisation.dump = {
              inheritParentConfig = true;
              configuration.services.forgejo.dump = {
                enable = true;
                type = "tar.zst";
                file = "dump.tar.zst";
              };
            };
          };
        client =
          { ... }:
          {
            programs.git = {
              enable = true;
              config = {
                user.email = "test@localhost";
                user.name = "test";
                init.defaultBranch = "main";
              };
            };
            programs.ssh.extraConfig = ''
              Host *
                StrictHostKeyChecking no
                IdentityFile ~/.ssh/privk
            '';
          };
      };

      testScript =
        { nodes, ... }:
        let
          inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
          serverSystem = nodes.server.system.build.toplevel;
          dumpFile =
            with nodes.server.specialisation.dump.configuration.services.forgejo.dump;
            "${backupDir}/${file}";
          remoteUri = "forgejo@server:test/repo";
          remoteUriCheckoutAction = "forgejo@server:test/checkout";

          actionsWorkflowYaml = ''
            run-name: dummy workflow
            on:
              push:
            jobs:
              cat:
                runs-on: native
                steps:
                  - uses: http://localhost:3000/test/checkout@main
                  - run: cat testfile
          '';
          # https://github.com/actions/checkout/releases
          checkoutActionSource = pkgs.fetchFromGitHub {
            owner = "actions";
            repo = "checkout";
            rev = "v4.1.1";
            hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos=";
          };
        in
        ''
          import json

          start_all()

          client.succeed("mkdir -p ~/.ssh")
          client.succeed("(umask 0077; cat ${snakeOilPrivateKey} > ~/.ssh/privk)")

          client.succeed("mkdir /tmp/repo")
          client.succeed("git -C /tmp/repo init")
          client.succeed("echo 'hello world' > /tmp/repo/testfile")
          client.succeed("git -C /tmp/repo add .")
          client.succeed("git -C /tmp/repo commit -m 'Initial import'")
          client.succeed("git -C /tmp/repo remote add origin ${remoteUri}")

          server.wait_for_unit("forgejo.service")
          server.wait_for_open_port(3000)
          server.wait_for_open_port(22)
          server.succeed("curl --fail http://localhost:3000/")

          server.succeed(
              "su -l forgejo -c 'gpg --homedir /var/lib/forgejo/data/home/.gnupg "
              + "--import ${toString (pkgs.writeText "forgejo.key" signingPrivateKey)}'"
          )

          assert "BEGIN PGP PUBLIC KEY BLOCK" in server.succeed("curl http://localhost:3000/api/v1/signing-key.gpg")

          api_version = json.loads(server.succeed("curl http://localhost:3000/api/forgejo/v1/version")).get("version")
          assert "development" != api_version and "${forgejoPackage.version}+gitea-" in api_version, (
              "/api/forgejo/v1/version should not return 'development' "
              + f"but should contain a forgejo+gitea compatibility version string. Got '{api_version}' instead."
          )

          server.succeed(
              "curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. "
              + "Please contact your site administrator.'"
          )
          server.succeed(
              "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo forgejo admin user create "
              + "--username test --password totallysafe --email test@localhost --must-change-password=false'"
          )

          api_token = server.succeed(
              "curl --fail -X POST http://test:totallysafe@localhost:3000/api/v1/users/test/tokens "
              + "-H 'Accept: application/json' -H 'Content-Type: application/json' -d "
              + "'{\"name\":\"token\",\"scopes\":[\"all\"]}' | jq '.sha1' | xargs echo -n"
          )

          server.succeed(
              "curl --fail -X POST http://localhost:3000/api/v1/user/repos "
              + "-H 'Accept: application/json' -H 'Content-Type: application/json' "
              + f"-H 'Authorization: token {api_token}'"
              + ' -d \'{"auto_init":false, "description":"string", "license":"mit", "name":"repo", "private":false}\'''
          )

          server.succeed(
              "curl --fail -X POST http://localhost:3000/api/v1/user/keys "
              + "-H 'Accept: application/json' -H 'Content-Type: application/json' "
              + f"-H 'Authorization: token {api_token}'"
              + ' -d \'{"key":"${snakeOilPublicKey}","read_only":true,"title":"SSH"}\'''
          )

          client.succeed("git -C /tmp/repo push origin main")

          client.succeed("git clone ${remoteUri} /tmp/repo-clone")
          print(client.succeed("ls -lash /tmp/repo-clone"))
          assert "hello world" == client.succeed("cat /tmp/repo-clone/testfile").strip()

          with subtest("Testing git protocol version=2 over ssh"):
              git_protocol = client.succeed("GIT_TRACE2_EVENT=true GIT_TRACE2_EVENT_NESTING=3 git -C /tmp/repo-clone fetch |& grep negotiated-version")
              version = json.loads(git_protocol).get("value")
              assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead."

          server.wait_until_succeeds(
              'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits '
              + '-H "Accept: application/json" | jq length)" = "1"',
              timeout=10
          )

          with subtest("Testing /metrics endpoint with token from cfg.secrets"):
              server.fail("curl --fail http://localhost:3000/metrics")
              server.succeed('curl --fail http://localhost:3000/metrics -H "Authorization: Bearer ${metricSecret}"')

          with subtest("Testing runner registration and action workflow"):
              server.succeed(
                  "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo forgejo actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token"
              )
              server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test")
              server.wait_for_unit("gitea-runner-test.service")
              server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'")

              # enable actions feature for this repository, defaults to disabled
              server.succeed(
                  "curl --fail -X PATCH http://localhost:3000/api/v1/repos/test/repo "
                  + "-H 'Accept: application/json' -H 'Content-Type: application/json' "
                  + f"-H 'Authorization: token {api_token}'"
                  + ' -d \'{"has_actions":true}\'''
              )

              # mirror "actions/checkout" action
              client.succeed("cp -R ${checkoutActionSource}/ /tmp/checkout")
              client.succeed("git -C /tmp/checkout init")
              client.succeed("git -C /tmp/checkout add .")
              client.succeed("git -C /tmp/checkout commit -m 'Initial import'")
              client.succeed("git -C /tmp/checkout remote add origin ${remoteUriCheckoutAction}")
              client.succeed("git -C /tmp/checkout push origin main")

              # push workflow to initial repo
              client.succeed("mkdir -p /tmp/repo/.forgejo/workflows")
              client.succeed("cp ${pkgs.writeText "dummy-workflow.yml" actionsWorkflowYaml} /tmp/repo/.forgejo/workflows/")
              client.succeed("git -C /tmp/repo add .")
              client.succeed("git -C /tmp/repo commit -m 'Add dummy workflow'")
              client.succeed("git -C /tmp/repo push origin main")

              def poll_workflow_action_status(_) -> bool:
                  try:
                      response = server.succeed("curl --fail http://localhost:3000/api/v1/repos/test/repo/actions/tasks")
                      status = json.loads(response).get("workflow_runs")[0].get("status")

                  except IndexError:
                      status = "???"

                  server.log(f"Workflow status: {status}")

                  if status == "failure":
                      raise Exception("Workflow failed")

                  return status == "success"

              with server.nested("Waiting for the workflow run to be successful"):
                  retry(poll_workflow_action_status, 60)

          with subtest("Testing backup service"):
              server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test")
              server.systemctl("start forgejo-dump")
              assert "Zstandard compressed data" in server.succeed("file ${dumpFile}")
              server.copy_from_vm("${dumpFile}")
        '';
    };
in
{
  mysql = runTest {
    imports = [ base ];
    _module.args.type = "mysql";
  };
  sqlite3 = runTest {
    imports = [ base ];
    _module.args.type = "sqlite3";
  };
  postgres = runTest {
    imports = [ base ];
    _module.args.type = "postgres";
  };
}
